package com.tsfyun.scm.service.impl.logistics;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.tsfyun.common.base.dto.TaskDTO;
import com.tsfyun.common.base.enums.domain.*;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.PageUtil;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.common.base.vo.CustomerSimpleVO;
import com.tsfyun.scm.dto.logistics.OverseasDeliveryNoteDTO;
import com.tsfyun.scm.dto.logistics.OverseasDeliveryNoteQTO;
import com.tsfyun.scm.dto.support.TaskNoticeContentDTO;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.customer.CustomerAbroadDeliveryInfo;
import com.tsfyun.scm.entity.logistics.OverseasDeliveryNote;
import com.tsfyun.scm.entity.logistics.OverseasDeliveryNoteMember;
import com.tsfyun.scm.entity.order.ExpOrder;
import com.tsfyun.scm.mapper.logistics.OverseasDeliveryNoteMapper;
import com.tsfyun.scm.service.common.ICommonService;
import com.tsfyun.scm.service.customer.ICustomerAbroadDeliveryInfoService;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.logistics.IOverseasDeliveryNoteMemberService;
import com.tsfyun.scm.service.logistics.IOverseasDeliveryNoteService;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.order.IExpOrderMemberService;
import com.tsfyun.scm.service.order.IExpOrderService;
import com.tsfyun.scm.service.support.ITaskNoticeContentService;
import com.tsfyun.scm.service.system.IStatusHistoryService;
import com.tsfyun.scm.vo.logistics.OverseasDeliveryNoteMemberVO;
import com.tsfyun.scm.vo.logistics.OverseasDeliveryNotePlusVO;
import com.tsfyun.scm.vo.logistics.OverseasDeliveryNoteVO;
import com.tsfyun.scm.vo.order.OrderCrossBorderStatusVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.tsfyun.common.base.validator.ValidatorUtils.isTrue;
import static com.tsfyun.common.base.validator.ValidatorUtils.isTrueCall;

/**
 * <p>
 * 香港送货单 服务实现类
 * </p>
 *
 *
 * @since 2021-11-09
 */
@Slf4j
@Service
public class OverseasDeliveryNoteServiceImpl extends ServiceImpl<OverseasDeliveryNote> implements IOverseasDeliveryNoteService {

    @Autowired
    private OverseasDeliveryNoteMapper overseasDeliveryNoteMapper;
    @Autowired
    private IOverseasDeliveryNoteMemberService overseasDeliveryNoteMemberService;
    @Autowired
    private ICustomerAbroadDeliveryInfoService customerAbroadDeliveryInfoService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ICommonService commonService;
    @Autowired
    private ITaskNoticeContentService taskNoticeContentService;
    @Autowired
    private IExpOrderService expOrderService;
    @Autowired
    private IExpOrderMemberService expOrderMemberService;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private IStatusHistoryService statusHistoryService;

    @Override
    public PageInfo<OverseasDeliveryNoteVO> pageList(OverseasDeliveryNoteQTO qto) {
        List<OverseasDeliveryNoteVO>  list = PageUtil.pageQ(qto, () -> overseasDeliveryNoteMapper.list(qto));
        Map<Long, CustomerSimpleVO> customerSimpleVOMap = customerService.obtainCustomerSimples(list.stream().map(OverseasDeliveryNoteVO::getCustomerId).collect(Collectors.toSet()));
        //获取客户名称
        list.stream().forEach(data-> data.setCustomerName(Optional.ofNullable(customerSimpleVOMap.get(data.getCustomerId())).map(CustomerSimpleVO::getName).orElse("")));
        return new PageInfo<>(list);
    }

    @Override
    public OverseasDeliveryNotePlusVO detail(Long id) {
        OverseasDeliveryNote overseasDeliveryNote = super.getById(id);
        Optional.ofNullable(overseasDeliveryNote).orElseThrow(()->new ServiceException("数据不存在"));
        List<OverseasDeliveryNoteMember> members = overseasDeliveryNoteMemberService.findByOverseasDeliveryNoteId(id);
        OverseasDeliveryNoteVO overseasDeliveryNoteVO = beanMapper.map(overseasDeliveryNote,OverseasDeliveryNoteVO.class);
        Customer customer = customerService.getById(overseasDeliveryNote.getCustomerId());
        overseasDeliveryNoteVO.setCustomerName(customer.getName());
        return new OverseasDeliveryNotePlusVO(overseasDeliveryNoteVO,
                beanMapper.mapAsList(members, OverseasDeliveryNoteMemberVO.class));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long saveMain(OverseasDeliveryNoteDTO dto) {
        OverseasDeliveryNote overseasDeliveryNote;
        if(Objects.isNull(dto.getId())) {
            overseasDeliveryNote = new OverseasDeliveryNote();
            overseasDeliveryNote.setStatusId(OverseasDeliveryNoteStatusEnum.WAIT_CONFIRM.getCode());
        } else {
            overseasDeliveryNote = super.getById(dto.getId());
            Optional.ofNullable(overseasDeliveryNote).orElseThrow(()->new ServiceException("境外送货单不存在"));
            DomainStatus.getInstance().check(DomainOprationEnum.EXP_OVERSEAS_DELIVERY_NOTE_EDIT,overseasDeliveryNote.getStatusId());
        }
        //修改不允许变更客户
        if(Objects.nonNull(dto.getId()) && !Objects.equals(dto.getCustomerId(),overseasDeliveryNote.getCustomerId())) {
            throw new ServiceException("不允许修改客户，如您制作境外送货单误选错客户，请删除该境外送货单重新制作");
        }
        overseasDeliveryNote.setDocNo(dto.getDocNo());
        overseasDeliveryNote.setDeliveryDate(dto.getDeliveryDate());
        overseasDeliveryNote.setCustomerId(dto.getCustomerId());
        overseasDeliveryNote.setRemark(dto.getRemark());
        overseasDeliveryNote.setReceivingNo(dto.getReceivingNo());
        overseasDeliveryNote.setReceivingWarehouse(dto.getReceivingWarehouse());

        CustomerAbroadDeliveryInfo customerAbroadDeliveryInfo = customerAbroadDeliveryInfoService.getById(dto.getOverseasTakeInfoId());
        Optional.ofNullable(customerAbroadDeliveryInfo).orElseThrow(()->new ServiceException("收货信息不存在，请重新选择"));
        TsfPreconditions.checkArgument(Objects.equals(customerAbroadDeliveryInfo.getCustomerId(),overseasDeliveryNote.getCustomerId()),new ServiceException("客户境外收货不匹配，请重新选择"));
        overseasDeliveryNote.setConsigneeId(customerAbroadDeliveryInfo.getId());
        overseasDeliveryNote.setConsignee(customerAbroadDeliveryInfo.getCompanyName());
        overseasDeliveryNote.setConsigneeLinkMan(customerAbroadDeliveryInfo.getLinkPerson());
        overseasDeliveryNote.setConsigneeLinkTel(customerAbroadDeliveryInfo.getLinkTel());
        overseasDeliveryNote.setConsigneeAddress(customerAbroadDeliveryInfo.getAddress());

        overseasDeliveryNote.setConsignor(dto.getConsignor());
        overseasDeliveryNote.setConsignorLinkMan(dto.getConsignorLinkMan());
        overseasDeliveryNote.setConsignorLinkTel(dto.getConsignorLinkTel());
        overseasDeliveryNote.setConsignorAddress(dto.getConsignorAddress());
        try {
            if (Objects.nonNull(dto.getId())) {
                super.updateById(overseasDeliveryNote);
            } else {
                super.saveNonNull(overseasDeliveryNote);
            }
        } catch (DuplicateKeyException e) {
            if(StringUtils.isNotEmpty(e.getMessage()) && e.getMessage().contains("UK_overseas_delivery_note_doc_no")) {
                throw new ServiceException("境外送货单号已经存在请修改");
            }else {
                throw new ServiceException("保存失败，请检查数据是否填写正确");
            }
        }
        return overseasDeliveryNote.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void confirm(TaskDTO dto) {
        //判断是否已经选择订单明细
        List<OverseasDeliveryNoteMember> members = overseasDeliveryNoteMemberService.findByOverseasDeliveryNoteId(Long.valueOf(StringUtils.null2EmptyWithTrim(dto.getDocumentId())));
        isTrue(CollUtil.isNotEmpty(members),()-> new ServiceException("请修改派送单选择需要派送的订单明细"));
        OverseasDeliveryNoteStatusEnum afterDoneStatus = OverseasDeliveryNoteStatusEnum.of(dto.getNewStatusCode());
        OverseasDeliveryNote overseasDeliveryNote = commonService.changeDocumentStatus(dto);
        Customer customer = customerService.getById(overseasDeliveryNote.getCustomerId());
        if(Objects.equals(afterDoneStatus,OverseasDeliveryNoteStatusEnum.WAIT_DELIVERY)) {
            //任务通知
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.OVERSEASDELIVERYNOTE.getCode());
            taskNoticeContentDTO.setDocumentId(overseasDeliveryNote.getId().toString());
            taskNoticeContentDTO.setCustomerId(overseasDeliveryNote.getCustomerId());
            DomainOprationEnum domainOprationEnum = DomainOprationEnum.EXP_OVERSEAS_DELIVERY_NOTE_DISTRIBUTION;
            taskNoticeContentDTO.setOperationCode(domainOprationEnum.getCode());
            taskNoticeContentDTO.setContent(StrUtil.format("客户【{}】境外派送单【{}】已完成确认，可以开始发车派送。", customer.getName(),overseasDeliveryNote.getDocNo()));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",overseasDeliveryNote.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delivery(TaskDTO dto) {
        OverseasDeliveryNoteStatusEnum afterDoneStatus = OverseasDeliveryNoteStatusEnum.of(dto.getNewStatusCode());
        OverseasDeliveryNote overseasDeliveryNote = commonService.changeDocumentStatus(dto);
        if(Objects.equals(OverseasDeliveryNoteStatusEnum.DELIVERING,afterDoneStatus)){
            //验证关联的订单对应的中港运输是否还未通车
            List<OrderCrossBorderStatusVO> orderCrossBorderStatusList = expOrderService.getTransportNoByOrderNo(Lists.newArrayList(overseasDeliveryNote.getExpOrderNos().split(",")));
            orderCrossBorderStatusList.stream().forEach(orderCrossBorderStatusVO -> isTrue(Objects.equals(CrossBorderWaybillStatusEnum.SIGNED,CrossBorderWaybillStatusEnum.of(orderCrossBorderStatusVO.getTransportStatus())),()->
                    new ServiceException(StringUtils.isNotEmpty(orderCrossBorderStatusVO.getTransportNo()) ?
                            StrUtil.format("订单【{}】对应的中港车，单号为【{}】，还未通关抵达境外，不允许境外发车派送",orderCrossBorderStatusVO.getOrderNo(),orderCrossBorderStatusVO.getTransportNo())
                            : StrUtil.format("订单【{}】还未绑定中港车，不允许境外发车派送",orderCrossBorderStatusVO.getOrderNo()))
            ));
        }
        Customer customer = customerService.getById(overseasDeliveryNote.getCustomerId());
        if(Objects.equals(afterDoneStatus,OverseasDeliveryNoteStatusEnum.DELIVERING)){
            //任务通知
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.OVERSEASDELIVERYNOTE.getCode());
            taskNoticeContentDTO.setDocumentId(overseasDeliveryNote.getId().toString());
            taskNoticeContentDTO.setCustomerId(overseasDeliveryNote.getCustomerId());
            DomainOprationEnum domainOprationEnum = DomainOprationEnum.EXP_OVERSEAS_DELIVERY_NOTE_SIGN;
            taskNoticeContentDTO.setOperationCode(domainOprationEnum.getCode());
            taskNoticeContentDTO.setContent(StrUtil.format("客户【{}】境外派送单【{}】正在派送中，派送到目的地后记得做派送完成。", customer.getName(),overseasDeliveryNote.getDocNo()));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",overseasDeliveryNote.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void reached(TaskDTO dto) {
        OverseasDeliveryNote overseasDeliveryNote = commonService.changeDocumentStatus(dto);
        List<String> orderNos = Lists.newArrayList(overseasDeliveryNote.getExpOrderNos().split(","));

        //判断该境外派送单下面的订单是否符合改成已签收的状态（必须该订单的所有的明细均已签收才可以）
        List<String> needSignedOrderNos = Lists.newArrayList();
        orderNos.stream().forEach(orderNo->{
            BigDecimal signedQuantity = overseasDeliveryNoteMapper.getOrderSignedQuantity(orderNo);
            BigDecimal totalQuantity = expOrderMemberService.getOrderQuantity(orderNo);
            log.info("订单【{}】已签收的派送数量【{}】,订单总数量【{}】",orderNo,signedQuantity,totalQuantity);
            isTrueCall(signedQuantity.compareTo(totalQuantity) >= 0,()->needSignedOrderNos.add(orderNo));
        });
        //修改订单状态、异步写入历史状态变更
        if(CollUtil.isNotEmpty(needSignedOrderNos)) {
            expOrderService.batchUpdateStatus(ExpOrderStatusEnum.SIGNED.getCode(),needSignedOrderNos);
            needSignedOrderNos.stream().forEach(orderNo->{
                threadPoolTaskExecutor.execute(()->{
                    ExpOrder expOrder = expOrderService.findByDocNo(orderNo);
                    statusHistoryService.saveHistory(DomainOprationEnum.EXP_OVERSEAS_DELIVERY_NOTE_SIGN,expOrder.getId().toString(),ExpOrder.class.getName(),
                            expOrder.getStatusId(),ExpOrderStatusEnum.of(expOrder.getStatusId()).getName(),
                            ExpOrderStatusEnum.SIGNED.getCode(),ExpOrderStatusEnum.SIGNED.getName(),
                            "境外派送签收自动触发","系统");
                });
            });
        }
    }

    @Override
    public void delete(Long id) {
        OverseasDeliveryNote overseasDeliveryNote = super.getById(id);
        Optional.ofNullable(overseasDeliveryNote).orElseThrow(()->new ServiceException("境外送货单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.EXP_OVERSEAS_DELIVERY_NOTE_DELETE,overseasDeliveryNote.getStatusId());
        //删除派送单的明细和派送单主单
        overseasDeliveryNoteMemberService.deleteByOverseasDeliveryNoteId(id);
        removeById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateExpOrderNos(Long id, String expOrderNos) {
        overseasDeliveryNoteMapper.updateExpOrderNos(id,expOrderNos);
    }

}
